from Tkinter import *
from time import time

class ToolTip(Toplevel):
    """
    Provides a ToolTip widget for Tkinter.
    To apply a ToolTip to any Tkinter widget, simply pass the widget to the
    ToolTip constructor
    """ 
    def __init__(self, widget, message=None, message_fnc=None, delay=1.0, follow=True):
        """
        Initialize the ToolTip
        
        Arguments:
          widget:      The widget this ToolTip is assigned to
          message:     A static string message assigned to the ToolTip
          message_fnc: A function that returns a string to use as the ToolTip text
          delay:       The delay in seconds before the ToolTip appears(may be float)
          follow:      If True, the ToolTip follows motion, otherwise hides
        """
        self.widget = widget
        self.parent = self.widget.master
        Toplevel.__init__(self, self.parent, bg='black', padx=1, pady=1)
        self.withdraw()              # initially hide the tooltip
        self.overrideredirect(True)  # instruct the window manager to NOT decorate this window
        
        self.message = StringVar(value="No tooltip provided" if message is None else message)
        self.message_fnc = message_fnc
        self.delay = delay
        self.follow = follow
        self.visible = False
        self.enter_time = None
        self.label = Label(self, textvariable=self.message, bg="#FFFFDD")
        self.label.grid()
        self.widget.bind("<Enter>", self.spawn, add=True)
        self.widget.bind("<Leave>", self.hide, add=True)
        self.widget.bind("<Motion>", self.move, add=True)
        
    def spawn(self, event=None):
        """
        Spawn the ToolTip.  This simply makes the ToolTip eligible for display.
        Usually this is caused by entering the widget
        
        Arguments:
          event: The event that called this funciton
        """
        self.enter_time = time()
        self.after(int(self.delay * 1000), self.show)
        
    def show(self):
        """
        Displays the ToolTip if the time delay has been long enough
        """
        if self.enter_time is not None and time() - self.enter_time >=  self.delay:
            if self.message_fnc is not None:
                message = self.message_fnc()
                if message is None:
                    self.hide()
                    return
                self.message.set(message)
            self.deiconify()
            self.visible = True
            
    def move(self, event):
        """
        Processes motion within the widget.
        
        Arguments:
          event: The event that called this function
        """
        if not self.follow:
            self.hide()
        self.show()
        if self.enter_time is not None:
            self.geometry("+%d+%d" % (event.x_root + 10, event.y_root + 10))
            
    def hide(self, event=None):
        """
        Hides the ToolTip.  Usually this is caused by leaving the widget
        
        Arguments:
          event: The event that called this function
        """
        self.withdraw()
        self.visible = False
        if event is not None:
            self.enter_time = None
            